/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package com.ssfeng.youxia.shiro.realm;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.ssfeng.youxia.entity.User;
import com.ssfeng.youxia.service.IUserService;
import com.ssfeng.youxia.shiro.ShiroUser;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.util.CollectionUtils;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * Realm that exists to support salted credentials. The JdbcRealm implementation
 * needs to be updated in a future Shiro release to handle this.
 */
public class SaltAwareRealm extends AuthorizingRealm {

	private static final Logger logger = LoggerFactory.getLogger(SaltAwareRealm.class);

	@Autowired
	private Environment environment;

	private IUserService userService;

	/****
	 * Shiro权限认证
	 * <p>
	 * Title: doGetAuthorizationInfo
	 * </p>
	 * <p>
	 * Description:
	 * </p>
	 * 
	 * @param principals
	 * @return
	 * @see org.apache.shiro.realm.AuthorizingRealm#doGetAuthorizationInfo(org.apache.shiro.subject.PrincipalCollection)
	 */
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
		ShiroUser user=(ShiroUser) principals.getPrimaryPrincipal();
		SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
		List<String> list = userService.getUserRoleCodes(user.getUserId());
		if (!CollectionUtils.isEmpty(list)) {
			Set<String> roleSet = new HashSet<>(list);
			authorizationInfo.setRoles(roleSet);
		}

		Set<String> permissions = new HashSet<>();
		authorizationInfo.setStringPermissions(permissions);
		return authorizationInfo;
	}

	/****
	 * Shiro登录认证(原理：用户提交 用户名和密码 --- shiro 封装令牌 ---- realm 通过用户名将密码查询返回 ---- shiro
	 * 自动去比较查询出密码和用户输入密码是否一致---- 进行登陆控制 )
	 * <p>
	 * Title: doGetAuthenticationInfo
	 * </p>
	 * <p>
	 * Description:
	 * </p>
	 * 
	 * @param token
	 * @return
	 * @throws AuthenticationException
	 * @see org.apache.shiro.realm.AuthenticatingRealm#doGetAuthenticationInfo(org.apache.shiro.authc.AuthenticationToken)
	 */
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {

		logger.info("Shiro开始登录认证");
		UsernamePasswordToken ptoken = (UsernamePasswordToken) token;
		QueryWrapper<User> queryWrapper = new QueryWrapper<>();
		queryWrapper.eq("name", ptoken.getUsername());
		User user = userService.getOne(queryWrapper);
		// 账号不存在
		if (user == null) {
			throw new UnknownAccountException("No account found for user [" + ptoken.getUsername() + "]");
		}

		if (!user.getStatus().equals(new Byte("0"))) {
			throw new AccountException("禁止登录");
		}
		
		String salt = environment.getProperty("salt");

		/***
		 * YouxiaUser 字段太多了，所以放一个字段少的类进去
		 */
		ShiroUser shiroUser = new ShiroUser();
		shiroUser.setUserName(user.getName());
		shiroUser.setUserId(user.getUserId());

		// 认证缓存信息
		SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(shiroUser, user.getPassword(),
				ByteSource.Util.bytes(salt), getName());
		return info;
	}

	@Autowired
	public void setUserService(IUserService userService) {
		this.userService = userService;
	}
}
